<template>
  <view ref="root" class="nut-ellipsis" @click="handleClick">
    <view v-if="!exceeded">{{ content }}</view>
    <view v-if="exceeded && !expanded"
    >
      {{ ellipsis && ellipsis.leading
      }}<span v-if="expandText" class="nut-ellipsis__text" @click.stop="clickHandle(1)">{{ expandText }}</span
      >{{ ellipsis && ellipsis.tailing }}
    </view>
    <view v-if="exceeded && expanded">
      {{ content }}
      <span v-if="expandText" class="nut-ellipsis__text" @click.stop="clickHandle(2)">{{ collapseText }}</span>
    </view>
  </view>
</template>
<script lang="ts">
import { ref, reactive, toRefs, onMounted, PropType, watch } from 'vue'
import { createComponent } from '@/packages/utils/create'
import { EllipsisDirection } from './types'
const { create } = createComponent('ellipsis')

type EllipsisedValue = {
  leading?: string
  tailing?: string
}

export default create({
  props: {
    content: {
      type: String,
      default: ''
    },
    direction: {
      type: String as PropType<EllipsisDirection>,
      default: 'end'
    },
    rows: {
      type: [Number, String],
      default: 1
    },
    expandText: {
      type: String,
      default: ''
    },
    collapseText: {
      type: String,
      default: ''
    },
    symbol: {
      type: String,
      default: '...'
    },
    lineHeight: {
      type: [Number, String],
      default: '20'
    }
  },
  emits: ['click', 'change'],

  setup(props, { emit }) {
    const root = ref(null)
    let container: any = null
    let maxHeight = 0 // 当行的最大高度
    const ellipsis = ref<EllipsisedValue>()
    const state = reactive({
      exceeded: false, // 是否超出
      expanded: false // 是否折叠
    })

    watch(
      () => props.content,
      (newV, oldVal) => {
        if (newV != oldVal) {
          createContainer()
        }
      }
    )

    onMounted(() => {
      createContainer()
    })

    // 创建虚拟 container，内容为 props.content 的内容
    const createContainer = () => {
      if (!root.value) return
      const originStyle = window.getComputedStyle(root.value)
      container = document.createElement('div')
      const styleNames: string[] = Array.prototype.slice.apply(originStyle)
      styleNames.forEach((name) => {
        container.style.setProperty(name, originStyle.getPropertyValue(name))
      })
      container.style.position = 'fixed'
      container.style.left = '999999px'
      container.style.top = '999999px'
      container.style.zIndex = '-1000'
      container.style.height = 'auto'
      container.style.minHeight = 'auto'
      container.style.maxHeight = 'auto'
      container.style.textOverflow = 'clip'
      container.style.whiteSpace = 'normal'
      container.style.webkitLineClamp = 'unset'
      container.style.display = 'block'
      const lineHeight = pxToNumber(originStyle.lineHeight === 'normal' ? props.lineHeight : originStyle.lineHeight)
      maxHeight = Math.floor(
        lineHeight * (Number(props.rows) + 0.5)
        + pxToNumber(originStyle.paddingTop)
        + pxToNumber(originStyle.paddingBottom)
      )

      container.innerText = props.content
      document.body.appendChild(container)
      calcEllipse()
    }

    // 计算省略号的位置
    const calcEllipse = () => {
      if (container.offsetHeight <= maxHeight) {
        state.exceeded = false
        document.body.removeChild(container)
      } else {
        state.exceeded = true
        const end = props.content.length

        const middle = Math.floor((0 + end) / 2)

        const ellipsised = props.direction === 'middle' ? tailorMiddle([0, middle], [middle, end]) : tailor(0, end)

        ;(ellipsis as any).value = ellipsised

        document.body.removeChild(container)
      }
    }
    // 计算 start/end 省略
    const tailor: (left: number, right: number) => EllipsisedValue = (left: number, right: number) => {
      const actionText = state.expanded ? props.collapseText : props.expandText
      const end = props.content.length

      if (right - left <= 1) {
        if (props.direction === 'end') {
          return {
            leading: props.content.slice(0, left) + props.symbol
          }
        } else {
          return {
            tailing: props.symbol + props.content.slice(right, end)
          }
        }
      }
      const middle = Math.round((left + right) / 2)
      if (props.direction === 'end') {
        container.innerText = props.content.slice(0, middle) + props.symbol + actionText
      } else {
        container.innerText = actionText + props.symbol + props.content.slice(middle, end)
      }

      if (container.offsetHeight <= maxHeight) {
        if (props.direction === 'end') {
          return tailor(middle, right)
        } else {
          return tailor(left, middle)
        }
      } else {
        if (props.direction === 'end') {
          return tailor(left, middle)
        } else {
          return tailor(middle, right)
        }
      }
    }
    // 计算 middle 省略
    const tailorMiddle: (leftPart: [number, number], rightPart: [number, number]) => EllipsisedValue = (
      leftPart: [number, number],
      rightPart: [number, number]
    ) => {
      const actionText = state.expanded ? props.collapseText : props.expandText
      const end = props.content.length
      if (leftPart[1] - leftPart[0] <= 1 && rightPart[1] - rightPart[0] <= 1) {
        return {
          leading: props.content.slice(0, leftPart[0]) + props.symbol,
          tailing: props.symbol + props.content.slice(rightPart[1], end)
        }
      }
      const leftPartMiddle = Math.floor((leftPart[0] + leftPart[1]) / 2)
      const rightPartMiddle = Math.ceil((rightPart[0] + rightPart[1]) / 2)

      container.innerText
        = props.content.slice(0, leftPartMiddle)
        + props.symbol
        + actionText
        + props.symbol
        + props.content.slice(rightPartMiddle, end)

      if (container.offsetHeight <= maxHeight) {
        return tailorMiddle([leftPartMiddle, leftPart[1]], [rightPart[0], rightPartMiddle])
      } else {
        return tailorMiddle([leftPart[0], leftPartMiddle], [rightPartMiddle, rightPart[1]])
      }
    }

    const pxToNumber = (value: string | null | number) => {
      if (!value) return 0
      const match = (value as string).match(/^\d*(\.\d*)?/)
      return match ? Number(match[0]) : 0
    }

    // 展开收起
    const clickHandle = (type: number) => {
      if (type == 1) {
        state.expanded = true
        emit('change', 'expand')
      } else {
        state.expanded = false
        emit('change', 'collapse')
      }
    }

    // 文本点击
    const handleClick = () => {
      emit('click')
    }

    return { ...toRefs(state), root, ellipsis, clickHandle, handleClick }
  }
})
</script>
